package pl.piomin.service.blockchain.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.EthAccounts;
import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
import org.web3j.protocol.core.methods.response.EthGetTransactionReceipt;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import pl.piomin.service.blockchain.model.BlockchainTransaction;

import java.io.IOException;
import java.math.BigInteger;

@Service
public class BlockchainService {

    private static final Logger LOGGER = LoggerFactory.getLogger(BlockchainService.class);

    private final Web3j web3j;

    public BlockchainService(Web3j web3j) {
        this.web3j = web3j;
    }

    /**
     * 处理区块链交易
     * 该方法负责将交易发布到区块链网络，并更新交易对象的状态
     *
     * @param trx 区块链交易对象，包含交易的相关信息
     * @return 返回处理后的区块链交易对象，包含是否接受交易和交易哈希等信息
     * @throws IOException 如果与区块链网络通信出现错误，则抛出IOException
     */
    public BlockchainTransaction process(BlockchainTransaction trx) throws IOException {

        // 获取当前区块链上的账户信息
        EthAccounts accounts = web3j.ethAccounts().send();

        // 获取指定账户的当前交易计数，用于设置交易的nonce值，防止交易重复
        EthGetTransactionCount transactionCount = web3j.ethGetTransactionCount(accounts.getAccounts().get(trx.getFromId()), DefaultBlockParameterName.LATEST).send();

        // 创建一笔以太坊交易，包含从哪个账户转账、当前交易计数、gas限额、接收账户和转账金额
        Transaction transaction = Transaction.createEtherTransaction(
                accounts.getAccounts().get(trx.getFromId()), transactionCount.getTransactionCount(), BigInteger.valueOf(trx.getValue()),
                BigInteger.valueOf(21_000), accounts.getAccounts().get(trx.getToId()), BigInteger.valueOf(trx.getValue()));

        // 发送交易到区块链网络，并获取响应结果
        EthSendTransaction response = web3j.ethSendTransaction(transaction).send();

        // 检查交易发送是否成功，如果有错误，则更新交易对象的状态为不接受，并记录日志
        if (response.getError() != null) {
            trx.setAccepted(false);
            LOGGER.info("Tx rejected: {}", response.getError().getMessage());
            return trx;
        }

        // 如果交易发送成功，则更新交易对象的状态为接受，并获取交易哈希
        trx.setAccepted(true);
        String txHash = response.getTransactionHash();
        LOGGER.info("Tx hash: {}", txHash);

        // 设置交易对象的哈希值
        trx.setId(txHash);

        // 根据交易哈希获取交易的收据信息
        EthGetTransactionReceipt receipt = web3j.ethGetTransactionReceipt(txHash).send();

        // 如果收据信息存在，则记录日志
        receipt.getTransactionReceipt().ifPresent(transactionReceipt -> LOGGER.info("Tx receipt:  {}", transactionReceipt.getCumulativeGasUsed().intValue()));

        // 返回处理后的交易对象
        return trx;
    }

}
